{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<center>\n",
    "<img src=\"../../img/ods_stickers.jpg\">\n",
    "# <center> Открытый курс по машинному обучению\n",
    "Автор материала: аналитик-разработчик в команде Яндекс.Метрики Мария Мансурова. Материал распространяется на условиях лицензии [Creative Commons CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/). Можно использовать в любых целях (редактировать, поправлять и брать за основу), кроме коммерческих, но с обязательным упоминанием автора материала."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# <center>Тема 2. Визуальный анализ данных с Python\n",
    "## <center>Часть 1. Обзор библиотек Seaborn, Matplotlib и Plotly\n",
    "\n",
    "\n",
    "В начале как всегда настроим окружение: импортируем все необходимые библиотеки и немного настроим дефолтное отображение картинок."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# отключим предупреждения Anaconda\n",
    "import warnings\n",
    "\n",
    "warnings.simplefilter(\"ignore\")\n",
    "\n",
    "# будем отображать графики прямо в jupyter'e\n",
    "%matplotlib inline\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "\n",
    "# графики в svg выглядят более четкими\n",
    "%config InlineBackend.figure_format = 'svg'\n",
    "\n",
    "# увеличим дефолтный размер графиков\n",
    "from pylab import rcParams\n",
    "\n",
    "rcParams[\"figure.figsize\"] = 8, 5\n",
    "import pandas as pd"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "После этого загрузим в `dataframe` данные, с которыми будем работать. Для примеров визуализаций я выбрала данные о продажах и оценках видео-игр с  [Kaggle Datasets](https://www.kaggle.com/rush4ratio/video-game-sales-with-ratings). Данные об оценках игр есть не для всех строк, поэтому сразу оставим только те записи, по которым есть полные данные."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.read_csv(\"../../data/video_games_sales.csv\").dropna()\n",
    "print(df.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df.info()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df[\"User_Score\"] = df.User_Score.astype(\"float64\")\n",
    "df[\"Year_of_Release\"] = df.Year_of_Release.astype(\"int64\")\n",
    "df[\"User_Count\"] = df.User_Count.astype(\"int64\")\n",
    "df[\"Critic_Count\"] = df.Critic_Count.astype(\"int64\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Всего в таблице 6825 объектов и 16 признаков для них. Посмотрим на несколько первых записей c помощью метода head, чтобы убедиться, что все распарсилось правильно. Для удобства я оставила только те признаки, которые мы будем в дальнейшем использовать."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "useful_cols = [\n",
    "    \"Name\",\n",
    "    \"Platform\",\n",
    "    \"Year_of_Release\",\n",
    "    \"Genre\",\n",
    "    \"Global_Sales\",\n",
    "    \"Critic_Score\",\n",
    "    \"Critic_Count\",\n",
    "    \"User_Score\",\n",
    "    \"User_Count\",\n",
    "    \"Rating\",\n",
    "]\n",
    "df[useful_cols].head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Прежде чем мы перейдем к рассмотрению методов библиотек seaborn и plotly, обсудим самый простой и зачастую удобный способ визуализировать данные из pandas dataframe — это воспользоваться функцией plot.\n",
    "Для примера построим график продаж видео игр в различных странах в зависимости от года. Для начала отфильтруем только нужные нам столбцы, затем посчитаем суммарные продажи по годам и у получившегося dataframe вызовем функцию plot без параметров. \n",
    "\n",
    "Реализация функции plot в pandas основана на библиотеке matplotlib. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df[[x for x in df.columns if \"Sales\" in x] + [\"Year_of_Release\"]].groupby(\n",
    "    \"Year_of_Release\"\n",
    ").sum().plot();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "C помощью параметра kind можно изменить тип графика, например, на bar chart. Matplotlib позволяет очень гибко настраивать графики. На графике можно изменить почти все, что угодно, но потребуется порыться в документации и найти нужные параметры. Например, параметра rot отвечает за угол наклона подписей к оси x."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "df[[x for x in df.columns if \"Sales\" in x] + [\"Year_of_Release\"]].groupby(\n",
    "    \"Year_of_Release\"\n",
    ").sum().plot(kind=\"bar\", rot=45);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Seaborn\n",
    "\n",
    "Теперь давайте перейдем к библиотеке seaborn. Seaborn — это по сути более высокоуровневое API на базе библиотеки matplotlib. Seaborn содержит более адекватные дефолтные настройки оформления графиков. Также в библиотеке есть достаточно сложные типы визуализации, которые в matplotlib потребовали бы большого количество кода.\n",
    "\n",
    "Познакомимся с первым таким \"сложным\" типом графиков pair plot (scatter plot matrix). Эта визуализация поможет нам посмотреть на одной картинке, как связаны между собой различные признаки. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# c svg pairplot браузер начинает тормозить\n",
    "%config InlineBackend.figure_format = 'png'\n",
    "sns.pairplot(\n",
    "    df[[\"Global_Sales\", \"Critic_Score\", \"Critic_Count\", \"User_Score\", \"User_Count\"]]\n",
    ");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Также с помощью `seaborn` можно построить распределение, для примера посмотрим на распределение оценок критиков `Critic_Score`. Для этого построим __`distplot`__. По default'у на графике отображается гистограмма и [kernel density estimation](https://en.wikipedia.org/wiki/Kernel_density_estimation)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%config InlineBackend.figure_format = 'svg'\n",
    "sns.distplot(df.Critic_Score);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Для того чтобы подробнее посмотреть на взаимосвязь двух численных признаков, есть еще и __`joint_plot`__ – это гибрид `scatter plot` и `histogram` (отображаются также гистограммы распределений признаков). Посмотрим на то, как связаны между собой оценка критиков `Critic_Score` и оценка пользователя `User_Score`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sns.jointplot(x=\"Critic_Score\", y=\"User_Score\", data=df, kind=\"scatter\");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Еще один полезный тип графиков – это __`box plot`__. Давайте сравним пользовательские оценки игр для топ-5 крупнейших игровых платформ."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "top_platforms = (\n",
    "    df.Platform.value_counts().sort_values(ascending=False).head(5).index.values\n",
    ")\n",
    "sns.boxplot(\n",
    "    y=\"Platform\", x=\"Critic_Score\", data=df[df.Platform.isin(top_platforms)], orient=\"h\"\n",
    ");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Думаю, стоит обсудить немного подробнее, как же понимать `box plot`. `Box plot` состоит из коробки (поэтому он и называется `box plot`), усиков и точек. Коробка показывает интерквантильный размах распределения, то есть соответственно 25% (`Q1`) и 75% (`Q3`) процентили. Черта внутри коробки обозначает медиану распределения. \n",
    "С коробкой разобрались, перейдем к усам. Усы отображают весь разброс точек кроме выбросов, то есть минимальные и максимальные значения, которые попадают в промежуток `(Q1 - 1.5*IQR, Q3 + 1.5*IQR)`, где `IQR = Q3 - Q1` - интерквантильный размах. Точками на графике обозначаются выбросы (`outliers`) - те значения, которые не вписываются в промежуток значений, заданный усами графика.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "И еще один тип графиков (последний из тех, которые мы рассмотрим в этой статье) - это __`heat map`__. `Heat map` позволяет посмотреть на распределение какого-то численного признака по двум категориальным. Визуализируем суммарные продажи игр по жанрам и игровым платформам."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "platform_genre_sales = (\n",
    "    df.pivot_table(\n",
    "        index=\"Platform\", columns=\"Genre\", values=\"Global_Sales\", aggfunc=sum\n",
    "    )\n",
    "    .fillna(0)\n",
    "    .applymap(float)\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sns.heatmap(platform_genre_sales, annot=True, fmt=\".1f\", linewidths=0.5);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Plotly\n",
    "\n",
    "Мы рассмотрели визуализации на базе библиотеки `matplotlib`. Однако, это не единственная опция для построения графиков на языке `python`. Познакомимся также с библиотекой __`plotly`__. `Plotly` - это open-source библиотека, которая позволяет строить интерактивные графики в jupyter.notebook'e без необходимости зарываться в javascript код. \n",
    "\n",
    "Прелесть интерактивных графиков заключается в том, что можно посмотреть точное численное значение при наведении мыши, скрыть неинтересные ряды в визуализации, приблизить определенный участок графика и т.д.\n",
    "\n",
    "Перед началом работы импортируем все необходимые модули и инициализируем `plotly` с помощью команды `init_notebook_mode`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import plotly\n",
    "import plotly.graph_objs as go\n",
    "from plotly.offline import download_plotlyjs, init_notebook_mode, iplot, plot\n",
    "\n",
    "init_notebook_mode(connected=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Для начала построим __`line plot`__ с динамикой числа вышедших игр и их продаж по годам. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "years_df = (\n",
    "    df.groupby(\"Year_of_Release\")[[\"Global_Sales\"]]\n",
    "    .sum()\n",
    "    .join(df.groupby(\"Year_of_Release\")[[\"Name\"]].count())\n",
    ")\n",
    "years_df.columns = [\"Global_Sales\", \"Number_of_Games\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В `plotly` строится визуализация объекта `Figure`, который состоит из данных (массив линий, которые в библиотеке называются `traces`) и оформления/стиля, за который отвечает объект `layout`. В простых случаях можно вызывать функцию `iplot` и просто от массива `traces`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "trace0 = go.Scatter(x=years_df.index, y=years_df.Global_Sales, name=\"Global Sales\")\n",
    "\n",
    "trace1 = go.Scatter(\n",
    "    x=years_df.index, y=years_df.Number_of_Games, name=\"Number of games released\"\n",
    ")\n",
    "\n",
    "data = [trace0, trace1]\n",
    "layout = {\"title\": \"Statistics of video games\"}\n",
    "\n",
    "fig = go.Figure(data=data, layout=layout)\n",
    "\n",
    "iplot(fig, show_link=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Также можно сразу сохранить график в виде html-файла."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plotly.offline.plot(fig, filename=\"years_stats.html\", show_link=False);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Посмотрим также на рыночную долю игровых платформ, расчитанную по количеству выпущенных игр и по суммарной выручке. Для этого построим __`bar chart`__."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "platforms_df = (\n",
    "    df.groupby(\"Platform\")[[\"Global_Sales\"]]\n",
    "    .sum()\n",
    "    .join(df.groupby(\"Platform\")[[\"Name\"]].count())\n",
    ")\n",
    "platforms_df.columns = [\"Global_Sales\", \"Number_of_Games\"]\n",
    "platforms_df.sort_values(\"Global_Sales\", ascending=False, inplace=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "trace0 = go.Bar(x=platforms_df.index, y=platforms_df.Global_Sales, name=\"Global Sales\")\n",
    "\n",
    "trace1 = go.Bar(\n",
    "    x=platforms_df.index,\n",
    "    y=platforms_df.Number_of_Games,\n",
    "    name=\"Number of games released\",\n",
    ")\n",
    "\n",
    "data = [trace0, trace1]\n",
    "layout = {\"title\": \"Share of platforms\"}\n",
    "\n",
    "fig = go.Figure(data=data, layout=layout)\n",
    "\n",
    "iplot(fig, show_link=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "В `plotly` можно построить и __`box plot`__. Рассмотрим различия оценок критиков в зависимости от жанра игры."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = []\n",
    "\n",
    "for genre in df.Genre.unique():\n",
    "    data.append(go.Box(y=df[df.Genre == genre].Critic_Score, name=genre))\n",
    "iplot(data, show_link=False)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
